#!/usr/bin/python

#
# Copyright 2012 - Tom Alessi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Quinico Nagios Plugin


"""


import datetime
import json
import urllib
from optparse import OptionParser


def keyword_rank_position():
    """Query Quinico for keyword_rank position changes"""

    url = 'http://%s/keyword_rank/dashboard?domain=%s&format=json1' % (options.quinico,options.domain)
    if options.debug:
        print 'Executing keyword_rank position changes for %s' % (options.domain)
        print 'URL: %s' % url

    try:
        request = urllib.urlopen(url)
    except Exception, e:
        print 'CRITICAL : Quinico API Error\nError occurred querying Quinico API (%s):%s' % (url,e)
        exit(2)

    response = request.read()

    if options.debug:
        print 'Response: %s' % response

    request.close()

    # Reference date (always today)
    now = datetime.datetime.today()
    date_now = now.strftime("%Y-%m-%d")

    # Move back x days
    then = datetime.timedelta(days=options.history)
    date_then = now - then
    date_then = date_then.strftime("%Y-%m-%d")

    if options.debug:
        print 'Looking for changes since %s' % (date_then)

    found = None
    count_up = None
    count_down = None
    count_unch = None

    raw_json = json.loads(response)
    for row in raw_json:
        if row[0] == date_then:
            found = True
            count_up = row[1]['up']
            count_down = row[1]['down']
            count_unch = row[1]['unch']
            break

    # If there is not enough data to process this request, quit with a warning
    if not found:
        print 'WARNING : Not enough data to process request for %s' % date_then
        exit(1)

    if options.debug:
        print 'up: %s' % count_up
        print 'down: %s' % count_down
        print 'unch: %s' % count_unch

    count_total = count_up + count_down + count_unch
   
    # If count_down is zero, don't try the math
    if count_down: 
        percent_change = ((float(count_down) / float(count_total)) * 100)

        # Round to two significant figures
        percent_change = round(percent_change, 2)
    else:
        percent_change = 0

    if percent_change > options.threshold:
        print 'CRITICAL : %s%% of keywords down since %s' % (percent_change,date_then)
        exit(2)
    else:
        print 'OK : %s%% of keywords down since %s'  % (percent_change,date_then)
        exit(0)


def seomoz_metric():
    """Query Quinico for seomoz metrics"""

    url = 'http://%s/seomoz/trends?url=%s&metric=%s&format=json' % (options.quinico,options.domain,options.metric)
    if options.debug:
        print 'Executing seomoz_metric for %s and metric %s' % (options.domain,options.metric)
        print 'URL: %s' % url

    try:
        request = urllib.urlopen(url)
    except Exception, e:
        print 'CRITICAL : Quinico API Error\nError occurred querying Quinico API (%s):%s' % (url,e)
        exit(2)

    response = request.read()

    if options.debug:
        print 'Response: %s' % response

    request.close()

    raw_json = json.loads(response)

    # The quinico API already gives us this data in date ascending order
    # If we don't have at least two, then there is not enough data
    if len(raw_json) < 2:
        print 'OK : Not enough data points'
        exit(0)

    count_now = raw_json[len(raw_json) - 1][options.metric]
    date_now = raw_json[len(raw_json) - 1]['date']
    count_then = raw_json[len(raw_json) - 2][options.metric]
    date_then = raw_json[len(raw_json) - 2]['date']

    # If there was no data on the second to last run (its a zero), then give a warning and quit
    # We cannot divide by zero
    if count_then == '0':
        print 'WARNING : Not enough data (%s:%s, %s:%s)' % (date_then,count_then,date_now,count_now)
        exit(1)

    percent_change = ((float(count_now) - float(count_then)) / float(count_then)) * 100

    if options.debug:
        print 'threshold : %s' % options.threshold
        print 'now       : %s' % count_now
        print 'then      : %s' % count_then
        print 'change    : %s' % percent_change

    # Round to two significant figures
    percent_change = round(percent_change, 2)

    if percent_change < options.threshold:
        print 'CRITICAL : %s%%\n%s:%s => %s:%s' % (percent_change,date_then,count_then,date_now,count_now)
        exit(2)
    else:
        print 'OK : %s%%\n%s:%s => %s:%s' % (percent_change,date_then,count_then,date_now,count_now)
        exit(0)


def webmaster_total():
    """Query Quinico for webmaster total crawl errors"""

    url = 'http://%s/webmaster/total?domain=%s&format=json' % (options.quinico,options.domain)
    if options.debug:
        print 'Executing webmaster_total for %s' % options.domain
        print 'URL: %s' % url

    try:
        request = urllib.urlopen(url)
    except Exception, e:
        print 'CRITICAL : Quinico API Error\nError occurred querying Quinico API (%s):%s' % (url,e)
        exit(2)

    response = request.read()

    if options.debug:
        print 'Response: %s' % response

    request.close()

    # Date we are looking for (always today unless overridden)
    now = datetime.datetime.today()
    date_now = now.strftime("%Y-%m-%d")

    # Move back x days
    then = datetime.timedelta(days=options.history)
    date_then = now - then
    date_then = date_then.strftime("%Y-%m-%d")

    if options.debug:
        print 'Looking for changes between %s and %s' % (date_then,date_now)

    count_now = ''
    count_then = ''

    raw_json = json.loads(response)
    for row in raw_json:
        if row['date'] == date_now:
            count_now = row['count']
             
        if row['date'] == date_then:
            count_then = row['count']

    # If there is not enough data to process this request, quit with a warning
    # If there were in fact no crawl errors on the previous or current date, that might
    # actually be good.
    if not count_now or not count_then:
        print 'WARNING : Not enough data (%s:%s, %s:%s)' % (date_then,count_then,date_now,count_now)
        exit(1)

    percent_change = ((float(count_now) - float(count_then)) / float(count_then)) * 100

    # Round to two significant figures
    percent_change = round(percent_change, 2)

    if options.debug:
        print 'threshold : %s' % options.threshold
        print 'now       : %s' % count_now
        print 'then      : %s' % count_then
        print 'change    : %s' % percent_change

    if percent_change > options.threshold:
        print 'CRITICAL : %s%%\n%s:%s => %s:%s' % (percent_change,date_then,count_then,date_now,count_now)
        exit(2)
    else:
        print 'OK : %s%%\n%s:%s => %s:%s' % (percent_change,date_then,count_then,date_now,count_now)
        exit(0)


#
# BEGIN MAIN PROGRAM EXECUTION
#


parser = OptionParser(description='Quinico Nagios Plugin', version='%prog 1.0')
parser.add_option("-c", "--command", dest="command", 
                  help="Command to run.             \
                  keyword_rank_position - query keyword rank position changes \
                  seomoz_metric - query seomoz metrics \
                  webmaster_total - query total crawl errors"
                 )
parser.add_option("-d", "--domain", dest="domain", help="Domain to check")
parser.add_option("-m", "--metric", dest="metric", help="Metric to check")
parser.add_option("-t", "--threshold", dest="threshold", type='int', help="Threshold, above/below which an error will occur")
parser.add_option("-H", "--history", dest="history", type='int', help="Number of days back to check")
parser.add_option("-q", "--quinico", dest="quinico", help="Quinico Server Fully Qualified Host Name")
parser.add_option('--debug',
                  action='store_true',
                  dest='debug',
                  default=False,
                  help='Print debug messages to standard out')
(options, args) = parser.parse_args()

# Run the requested command
if options.command == 'webmaster_total':
    webmaster_total()
elif options.command == 'seomoz_metric':
    seomoz_metric()
elif options.command == 'keyword_rank_position':
    keyword_rank_position()

# If we got this far, there was an error
print 'CRITICAL : unknown plugin error'
exit(2)
